#ifndef __tenacitas_log__text_line__h__
#define __tenacitas_log__text_line__h__


/** @file
    In this file is implemented class tenacitas::log::text_writer */

/** @todo */

// ==> C++ Headers
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include <fstream>
#include <chrono>

// ==> 3rds Headers
#include <boost/shared_ptr.hpp>

// ==> Our Headers
#include "level.h"
#include <tenacitas.exception/exception.h>
#include <tenacitas.boost_tools/utils.h>

// ==> Namespaces
using namespace std;

// ==> Macro Constants

// ==> Macro Commands


/** namespace of the organization */
namespace tenacitas {
    /** namespace for the project */
    namespace log {

        // ==> Typedefs 

        // ==> Pre-Declarations

        // ==> Attributes 

        // ==> Functions
        
        // ==> Classes

        /** Implementation of @p t_writer template parameter for
            tenacitas::log::file template class

            This log writer writes in a text file.

            The header of the file is defined by the method <tt>get_header( )</tt>
            
            The the header of each log  line has this format:
            <tt>level-name|nanoseconds-time-stamp|thread-id|file-name|function-name|line-number|</tt> 

            The log message is written after the log line header, and if it is
            composed by many parts, by using the <tt><<</tt> operator, each part
            will be separated by @p |. 

            For example:
            <code>
            log_debug( logger ) << "hello world! " << "it is great to see you!";
            </code>

            will create a line in the log file like this:
            <tt>
            debug|1371041696390809660|141830208|hasher.cpp|md5|345|hello world!|it is great to see you! 
            </tt>

            So, @p debug is the string corresponding to tenacitas::level::debug
            level; @p 1371041696390809660 is the time_stamp of the message; @p
            141830208 is the thread identifier; @p hasher.cpp is the name of the
            file; @p md5 is the name of the function; @p 345 is the line number;
            <tt>hello world!</tt> is the first part of the log message; <tt>it is
            great to see you!</tt> is the second part

            <b>A word to the programmer user</b>
            You, dear programer, won't need to worry about this class, as it will
            be used directly by tenacitas::log::file class.
            However, if you need to create your own log writer, it is a good idea
            to study how this log writer is implemented
        */
        class text_line {

            // ==> text_line - Friends

            /** Outputing a log line

                @throw tenacitas::exception::exception */
            friend std::ostream & operator << ( std::ostream & p_out,
                                                const text_line & p_line ) {
                try {
                    p_out << p_line.m_line;
                }
                catch( std::exception & l_ex ) {
                    std::cerr << "Error at " 
                              << __FILE__ << ", "
                              << __func__ << ", "
                              << __LINE__ << ": "
                              << "could not write to stream: "
                              << l_ex.what( ) << std::endl;
                    tenacitas::exception::exception l_ten_ex =
                        make_tenacitas_exception( l_ex.what( ) );
                    
                    throw l_ten_ex;
                }
                return p_out;
            }

        public:

            // ==> text_line - Public Internal Types

            /** Type of pointer to @p text_line */
            typedef boost::shared_ptr<text_line> ptr;

            // ==> text_line - Public Constructors

            /** */
            inline ~text_line( );

            /** Creates a pointer to a text_line 

                @throw tenacitas::exception::exception */
            inline static ptr create( );

            // ==> text_line - Public Accessors

            /** Retrieves the header of a text file */
            static const std::string & get_header( );

            // ==> text_line - Public Processors

            /** Writes the log line header to a text file

                @param p_file is the log text file handler where the log
                line header will be written
                @param p_thread_id is the id of the thread where the log message
                is being written;
                @param p_level is the level of the log message;
                @param p_file_name is the name of the file where the log
                message is being generated;
                @param p_function is the function where the log message is
                being generated;
                @param p_line is the line number in @p p_file_name where the
                log message is being generated

                @throw tenacitas::exception::exception
            */
            inline void new_line( ofstream & p_file,
                                  boost::thread::id p_thread_id,
                                  level p_level,
                                  const char * p_file_name,
                                  const char * p_function,
                                  int64_t p_line ) const;
            
            /** Opens the text file

                @param p_file is the log text file handler to be opened
                @param p_name is the name of the log file

                @note As @p text_line is manipulated only from
                tenacitas::log::file class, some checks are not done

               @throw tenacitas::exception::exception
            */
            inline void open_file( ofstream & p_file,
                                   const std::string & p_name );

            /** Closes the file

                @param p_file is the log text file handler where the log
                was being written
             */
            inline void close_file( ofstream & p_file );

            /** Writes data to the file
                Writes any data type that to the file, according to the
                @p t_line_formater class

                @param p_file is the log text file handler where the
                data will be written
                @param  p_data is the data to be written to the file

                @throw tenacitas::exception::exception
            */
            template <typename t_data>
            inline void write ( ofstream & p_file,
                                const t_data & p_data ) const;

            /** Reads a line from a existing text log file

                @param p_file is the log file that is being read 

                @throw tenacitas::exception::exception */
            inline bool read( ifstream & p_file );

            /** Informs if a line read is a header line, i.e., contains @p
                m_text_log_identifier

                @throw tenacitas::exception::exception */
            inline bool is_header( );
                
            // ==> text_line - Public Operators

            /** Less-than operator
                @throw tenacitas::exception::exception */
            inline bool operator < ( const text_line & p_stl ) const;
            
            /** Equal-to operator */
            inline bool operator==( const text_line & p_r ) const;
            
            /** Equal-to operator */
            inline bool operator==( const std::string & p_r ) const;
            
        private:

            // ==> text_line - Private Processors

            /** Retrieves the timestamp from the line read, if possible

                @throw tenacitas::exception::exception */
            bool get_timestamp( uint64_t & p_timestamp ) const;
            
            // ==> text_line - Private Attributes

            /** Line read */
            std::string m_line;
        };
    }
}

// =============================================================================
// I M P L E M E N T A T I O N
// =============================================================================

using namespace tenacitas::log;

// -----------------------------------------------------------------------------
text_line::
~text_line( ) { }

// -----------------------------------------------------------------------------
text_line::ptr
text_line::
create( ) {
    ptr l_rec;

    try {
        l_rec = ptr( new text_line( ) );
    }
    catch( std::exception & l_ex ) {
        std::cerr << "Error at " 
                  << __FILE__ << ", "
                  << __func__ << ", "
                  << __LINE__ << ": "
                  << "could not alocate memory: "
                  << l_ex.what( ) << std::endl;
        tenacitas::exception::exception l_ten_ex =
            make_tenacitas_exception( l_ex.what( ) );
                    
        throw l_ten_ex;
    }
    return l_rec;
}


// -----------------------------------------------------------------------------
void
text_line::
new_line( ofstream & p_file,
          boost::thread::id p_thread_id,
          level p_level,
          const char * p_file_name,
          const char * p_function,
          int64_t p_line ) const {
    try {
        if ( p_file.is_open( ) ) {
            p_file << std::endl << p_level.str( ) << "|"
                   << std::chrono::nanoseconds (
                       std::chrono::high_resolution_clock::now().
                       time_since_epoch( ) ).count( ) 
                   << "|" << tenacitas::boost_tools::th2int( p_thread_id )
                   << "|" << p_file_name
                   << "|" << p_function
                   << "|" << p_line
                   << "|";
            p_file.flush( );
        }
        
    }
    catch ( std::exception & l_ex ) {
        tenacitas::exception::exception l_ten_ex =
            make_tenacitas_exception( l_ex.what( ) );
                    
        throw l_ten_ex;
    }                
}

// -----------------------------------------------------------------------------
void
text_line::
open_file( ofstream & p_file, const std::string & p_name ) {
    try {
        if ( p_file.is_open( ) ) {
            p_file.close( );
        }        
        p_file.open( p_name.c_str( ) );
        p_file << get_header( );
    }
    catch ( std::exception & l_ex ) {
        tenacitas::exception::exception l_ten_ex =
            make_tenacitas_exception( l_ex.what( ) );
                    
        throw l_ten_ex;
    }                
}

// -----------------------------------------------------------------------------
void
text_line::
close_file( ofstream & p_file ) {
    if ( p_file.is_open( ) ) {
        p_file << std::endl;
        p_file.close( );
    }    
}

// -----------------------------------------------------------------------------
template <typename t_data>
void
text_line::
write ( ofstream & p_file, const t_data & p_data ) const {
    try {
        if ( p_file.is_open( ) ) {
            p_file << p_data;
            p_file.flush( );
        }
    }
    catch ( std::exception & l_ex ) {
        tenacitas::exception::exception l_ten_ex =
            make_tenacitas_exception( l_ex.what( ) );
                    
        throw l_ten_ex;
    }                
}

// -----------------------------------------------------------------------------
bool
text_line::
read( ifstream & p_file ) {
    try {
        if ( p_file.is_open( ) ) {
            std::getline( p_file, m_line );

            if ( ( p_file.eof( ) ) || ( m_line.size( ) == 0 ) ) {
                return false;
            }
            return true;
        }
        return false;
    }                
    catch ( std::exception & l_ex ) {
        tenacitas::exception::exception l_ten_ex =
            make_tenacitas_exception( l_ex.what( ) );
                    
        throw l_ten_ex;
    }                
}

// -----------------------------------------------------------------------------
bool
text_line::
is_header( ) {
    return ( m_line.find( get_header( ) ) != std::string::npos );
}

// -----------------------------------------------------------------------------
bool
text_line::
operator < ( const text_line & p_stl ) const {
    bool l_rc = false;
    
    try {
        uint64_t l_this_timestamp;
        uint64_t l_other_timestamp;
                    
        if ( ( get_timestamp( l_this_timestamp ) ) &&
             ( p_stl.get_timestamp( l_other_timestamp ) ) ) {
            l_rc = ( l_this_timestamp < l_other_timestamp );
        }
    }
    catch ( tenacitas::exception::exception & l_ex ) {
        std::cerr << "Error at " 
                  << __FILE__ << ", "
                  << __func__ << ", "
                  << __LINE__ << ": "
                  << l_ex.what( ) << std::endl;
        throw l_ex;
    }
    return l_rc;
}

// -----------------------------------------------------------------------------
bool
text_line::
operator==( const text_line & p_r ) const {
    return ( m_line == p_r.m_line );
}


// -----------------------------------------------------------------------------
bool
text_line::
operator==( const std::string & p_r ) const {
    return ( m_line == p_r );
}
            


#endif
 // end 
